使用CXF实现基于Soap协议的WebService

您所在的位置:网站首页 apache CXF wdsl 特殊字符 使用CXF实现基于Soap协议的WebService

使用CXF实现基于Soap协议的WebService

2024-01-25 14:47| 来源: 网络整理| 查看: 265

本文介绍使用CXF实现基于Soap协议的WebService(CXF的版本是3.0.0)

一. 前言

Java有三种WebService规范:Jax-WS,Jax-RS,Jaxm

1. Jax-WS(Java Api for XML-Based WebService):实现Soap协议(Simple Object Access Protocol)(用的也不多了)2. Jax-RS(Java Api for Resource-Based WebService):实现Rest方式(Representational State Transfer)(推荐)3. Jaxm支持文件传输,暴露更多底层细节(不推荐)

二. 引入依赖

  org.apache.cxf  cxf-rt-frontend-jaxws  3.0.0

  org.apache.cxf   cxf-rt-transports-http  3.0.0

  org.apache.cxf  cxf-rt-transports-http-jetty  3.0.0

三. 编写SEI(Service Endpoint Interface)

1. 接口

@WebServicepublic interface HelloWorld {

  //方法名和接口名可以不一致,默认一致,如果一致,可以省略operationName,甚至省略WebMethod  @WebMethod(operationName = "sayHiString")  public String sayHelloString(@WebParam(name = "name") String name);

  //对象参数User代码略,User的定义可能不在HelloWorld的包中,在或不在,对后期调用会产生影响  @WebMethod(operationName = "sayHiUser")  public User sayHelloUser(User user);

}

2. 实现类

//endpointInterface的值是接口的全名

@WebService(endpointInterface = "net.jmystudio.cxf.soap.HelloWorld", serviceName = "HelloWorld")public class HelloWorldImpl implements HelloWorld {

  @Override  public String sayHelloString(String name) {    return "Hello, (String) " + name;  }

  @Override  public User sayHelloUser(User user) {    return new User("Soap_" + user.getName());  }

}

四. 发布服务

1. 发布方式一:使用默认的Jetty时,使用Java内置的Endpoint

javax.xml.ws.Endpoint.publish("http://localhost:8088/testcxf/cxf/soap/hello1", new HelloWorldImpl());

 (http://localhost:8088/testcxf/cxf/soap/hello1?wsdl)

 2. 发布方式二:使用默认的Jetty时,使用CXF提供的JaxWsServerFactoryBean(与Rest类似)

// 1). 服务端工厂类JaxWsServerFactoryBean server = new JaxWsServerFactoryBean();

// 2). 设置了二个属性(setServiceClass可省)server.setAddress("http://localhost:8088/testcxf/cxf/soap/hello2");server.setServiceBean(new HelloWorldImpl());

// 添加输入&输出日志(可选)server.getInInterceptors().add(new LoggingInInterceptor());server.getOutInterceptors().add(new LoggingOutInterceptor());

// 3). 创建并发布服务,会发起一个http服务,默认使用Jettyserver.create();

(http://localhost:8088/testcxf/cxf/soap/hello2?wsdl)

3. 发布方式三:在Web应用中,使用CXFNonSpringServlet发布(实际是显式调用了发布方式一(或二))

a. 在web.xml中添加CXFNonSpringServlet的实现类(与Rest一样)

  CXFNonSpring  net.jmystudio.servlet.WebServiceNonSpringServlet  CXFNonSpring  /cxfns/*

b. 实现WebServiceNonSpringServlet类(与Rest类似)

import org.apache.cxf.transport.servlet.CXFNonSpringServlet;

public class WebServiceNonSpringServlet extends CXFNonSpringServlet {

  @Override  protected void loadBus(ServletConfig servletConfig) {    super.loadBus(servletConfig);    //使用Endpoint,代码类似发布方式一,也可以使用JaxWsServerFactoryBean,代码类似发布方式二,此处略    javax.xml.ws.Endpoint.publish("/soap/hello3", new HelloWorldImpl());  }

}

(http://localhost:8090/testcxf/cxfns/soap/hello3?wsdl)(应用包名是testcxf)

4. 发布方式四:在Web应用中,整合Spring+CXFServlet发布(实际是隐式调用了发布方式一(或二))(最常用)

a. 需要引入Spring相关的Jar包(与Rest一样)

  org.springframework   spring-context   4.1.6.RELEASE   org.springframework   spring-web   4.1.6.RELEASE

b. 在web.xml中添加Spring配置和CXFServlet(与Rest一样)

  contextConfigLocation   classpath:spring*.xml   org.springframework.web.context.ContextLoaderListener

  CXF   org.apache.cxf.transport.servlet.CXFServlet   CXF   /cxf/*

c.添加关于cxf的spring配置文件(例spring-cfx-soap.xml)(与Rest类似)

                     

                     

(http://localhost:8090/testcxf/cxf/soap/hello5?wsdl) (应用包名是testcxf)(http://localhost:8090/testcxf/cxf/soap/hello6?wsdl) 

五. 调用方式

无论使用哪种发布方式,发布成功后,在浏览器中输入 $address+"?wsdl",均可看到该WebService的接口定义的详细内容

例如 http://localhost:8088/testcxf/cxf/soap/hello1?wsdl

代码调用主要有2种方式

1. 调用方式一:使用JaxWsProxyFactoryBean获得静态Client,(显式依赖WebService接口,需要引入服务提供方提供的jar包)(不推荐)(与Rest类似)

 // 1). 客户端工厂类JaxWsProxyFactoryBean proxy = new JaxWsProxyFactoryBean();

 // 2). 设置了两个属性proxy.setAddress("http://localhost:8088/testcxf/cxf/soap/hello1");proxy.setServiceClass(HelloWorld.class);

// 添加输入&输出日志(可选)proxy.getInInterceptors().add(new LoggingInInterceptor());proxy.getOutInterceptors().add(new LoggingOutInterceptor());

// 3). 创建会发起一个http请求HelloWorld  staticClient =  (HelloWorld) proxy.create();

// 4). 调用方法,获得数据System.out.println(staticClient.sayHelloString("Jimmy"));System.out.println(staticClient.sayHelloUser(new User("Tony")));

2. 调用方式二:使用JaxWsDynamicClientFactory获得动态Client,(无需引入服务提供方提供的jar包)

// 1). 根据接口定义url,获得ClientJaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();Client dynamicClient = clientFactory.createClient("http://localhost:8088/testcxf/cxf/soap/hello1?wsdl");

// 2). 调用接口,获得数据 //sayHiString是WebMethod中定义的接口名,不是方法名Object[] result = dynamicClient.invoke("sayHiString", "Kevin"); System.out.println(result[0]);result = dynamicClient.invoke("sayHiUser", new User("Cherry"));System.out.println(result[0]);

Note: 调用方式二中,参数是对象的情况需要特别注意,User的定义是否在SEI(此例是HelloWorld)的包中?

如果发布时不指定特定的targetNamespace的话,默认的targetNamespace是SEI的所在包名(逆序),而不是参数User所在包名(2个包名可以不一样的),这时调用的时候,必须传递定义在targetNamespace的User(不是发布时代码中的User,具体可查看该接口的wsdl,会发现此User非彼User),不然会报两个User的java.lang.ClassCastException

解决办法是:

a.最简单:发布时,定义的User和SEI同在一个包中(推荐)b.动态解析wsdl,需要获得ServiceInfo/BindingInfo/BindingOperationInfo/BindingMessageInfo/MessagePartInfo/PropertyDescriptor等一系列对象,逐步获得接口定义的详细内容后,动态生成参数对象,难度很大(不推荐)

3. 其它调用方式:使用HttpClient或HttpURLConnection等连接,与常规的URL获得数据的方式一致,详情略。(与Rest一样)

 

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3